package com.netvine.iias;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.callback.impl.DefaultDisconnectCallback;
import com.netsdk.lib.callback.impl.DefaultHaveReconnectCallBack;
import com.netsdk.lib.enumeration.ENUMERROR;
import com.netsdk.module.BaseModule;
import com.sun.jna.ptr.IntByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class DHSDKService implements SDKservice {
    private static final Logger log = LoggerFactory.getLogger(run.class);
    @Value("${nvr.ip}")
    private String ip;

    @Value("${nvr.port}")
    private Integer port;

    @Value("${nvr.account}")
    private String account;

    @Value("${nvr.password}")
    private String password;
    static BaseModule baseModule = new BaseModule();
    // ip, m_hLoginHandle
    public static Map<String, NetSDKLib.LLong> m_hLoginHandleMap = new ConcurrentHashMap<>();

    private static final int DEFAULT_SPEED = 4;

    private static final int DEFAULT_PRESET_NUM = 1;

    @PostConstruct
    public void init() {
        boolean bInit = baseModule.init(
                DefaultDisconnectCallback.getINSTANCE(), DefaultHaveReconnectCallBack.getINSTANCE(), false);
        if (!bInit) {
            log.error("CLIENT_Init failed!");
            logoutAndCleanUp(bInit, false);
        }
    }

    @Override
    public List<CameraInfo> getCameraList() {
        List<CameraInfo> result = new ArrayList<>();
        login(ip, port, account, password);
        ArrayNode deviceList = getDeviceList(ip);
        int maxChannelNum = deviceList.size();
        for (int i = 0; i < maxChannelNum; i++) {
            if (deviceList.get(i).get("Enable").asBoolean()) {
                CameraInfo videoDeviceDto = new CameraInfo();
                videoDeviceDto.setIp(deviceList.get(i).get("Address").asText());
                videoDeviceDto.setPort(deviceList.get(i).get("Port").asText());
                videoDeviceDto.setAccount(deviceList.get(i).get("UserName").asText());
                videoDeviceDto.setChanNum(String.valueOf(i));
                String fullRtsp = "rtsp://" + account + ":" + password
                        + "@" + ip
                        + "/cam/realmonitor?channel=" + (i + 1) + "&subtype=0";
                videoDeviceDto.setRtsp(fullRtsp);
                result.add(videoDeviceDto);
            }

        }
        return result;
    }

    @Override
    public Result PTZControl(PtzDto ptzDto) {
        int dhCommand = getDaHuaCommand(ptzDto.getCommand());
        if (dhCommand == -1) {
            log.error("getDaHuaCommand failed!");
            throw new RuntimeException("getDaHuaCommand failed");

        }
//        if (!commandParms.isResult()) {
//            throw new RuntimeException(commandParms.getMessage());
//        }
        boolean controlResult = false;
        if (login(ip, port, account, password)) {
            NetSDKLib.LLong m_hLoginHandle = m_hLoginHandleMap.get(ip);
            if (dhCommand >= NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_LEFTTOP &&
                    dhCommand <= NetSDKLib.NET_EXTPTZ_ControlType.NET_EXTPTZ_RIGHTDOWN) {
                // 水平+竖直
                controlResult = baseModule.getNetsdkApi().CLIENT_DHPTZControlEx(m_hLoginHandle, Integer.parseInt(ptzDto.getChanNum()),
                        dhCommand, DEFAULT_SPEED, DEFAULT_SPEED, 0, ptzDto.getIsStop());
            } else {
                // 水平或竖直
                controlResult = baseModule.getNetsdkApi().CLIENT_DHPTZControlEx(m_hLoginHandle, Integer.parseInt(ptzDto.getChanNum()),
                        dhCommand, 0, DEFAULT_SPEED, 0, ptzDto.getIsStop());
            }
        }
        if (!controlResult) {
            log.error("ptz control failed!, error:{}", ENUMERROR.getFullError());
            throw new RuntimeException(ENUMERROR.getFullError());
        }
        return Result.ok();
    }

    @Override
    public Result zoomControl(PtzDto ptzDto) {
        int dhCommand = getDaHuaCommand(ptzDto.getCommand());
        if (dhCommand != NetSDKLib.NET_PTZ_ControlType.NET_PTZ_ZOOM_ADD_CONTROL &&
                dhCommand != NetSDKLib.NET_PTZ_ControlType.NET_PTZ_ZOOM_DEC_CONTROL) {
            log.error("getDaHuaCommand failed!");
            throw new RuntimeException("get dahua command failed");
        }
        boolean controlResult = false;
        if (login(ip, port,
                account, password)) {
            NetSDKLib.LLong m_hLoginHandle = m_hLoginHandleMap.get(ip);
            controlResult = baseModule.getNetsdkApi().CLIENT_DHPTZControlEx(m_hLoginHandle, Integer.parseInt(ptzDto.getChanNum()),
                    dhCommand, 0, DEFAULT_SPEED, 0, ptzDto.getIsStop());
        }
        if (!controlResult) {
            log.error("zoom control failed!, error:{}", ENUMERROR.getFullError());
            throw new RuntimeException(ENUMERROR.getFullError());
        }
        return Result.ok();
    }

    @Override
    public Result presetPoint(PtzDto ptzDto) {
        int dhCommand = getDaHuaCommand(ptzDto.getCommand());
        if (dhCommand != NetSDKLib.NET_PTZ_ControlType.NET_PTZ_POINT_MOVE_CONTROL &&
                dhCommand != NetSDKLib.NET_PTZ_ControlType.NET_PTZ_POINT_SET_CONTROL) {
            log.error("getDaHuaCommand failed!");
            throw new RuntimeException("get dahua command failed");
        }
        boolean controlResult = false;
        if (login(ip, port,
                account, password)) {
            NetSDKLib.LLong m_hLoginHandle = m_hLoginHandleMap.get(ip);
            controlResult = baseModule.getNetsdkApi().CLIENT_DHPTZControlEx(m_hLoginHandle, Integer.parseInt(ptzDto.getChanNum()),
                    dhCommand, 0, DEFAULT_PRESET_NUM, 0, 0);
        }
        if (!controlResult) {
            log.error("preset control failed!, error:{}", ENUMERROR.getFullError());
            throw new RuntimeException(ENUMERROR.getFullError());
        }
        return Result.ok();
    }

    private ArrayNode getDeviceList(String ip) {
        ObjectMapper mapper = new ObjectMapper();
        IntByReference error = new IntByReference(0);
        int nBufferLen = 2097152;
        byte[] strBuffer = new byte[nBufferLen];
        boolean b = baseModule.getNetsdkApi().CLIENT_GetNewDevConfig(m_hLoginHandleMap.get(ip), NetSDKLib.CFG_CMD_REMOTEDEVICE,
                -1, strBuffer, nBufferLen, error, 5000, null);

        try {
            JsonNode jsonNode = mapper.readTree(new String(strBuffer, "UTF-8"));
            log.info("getDeviceList success, jsonNode:{}", jsonNode.get("params").get("table"));
            JsonNode rootNode = jsonNode.get("params").get("table");
            ArrayNode arrayNode = mapper.createArrayNode();

            // 遍历 rootNode，提取每个值并添加到 ArrayNode 中
            rootNode.fields().forEachRemaining(entry -> {
                arrayNode.add(entry.getValue());
            });
            return arrayNode;
        } catch (JsonProcessingException | UnsupportedEncodingException e) {
            log.error(ENUMERROR.getFullError());
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static boolean login(String ip, int port, String username, String password) {
        if (m_hLoginHandleMap.containsKey(ip)) {
            log.info("nvr {} already login", ip);
            return true;
        }
        NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY loginInput = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
        NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY loginOutput = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();

        // 填充登录信息
        System.arraycopy(ip.getBytes(), 0, loginInput.szIP, 0, ip.getBytes().length);
        loginInput.nPort = port;
        System.arraycopy(username.getBytes(), 0, loginInput.szUserName, 0, username.getBytes().length);
        System.arraycopy(password.getBytes(), 0, loginInput.szPassword, 0, password.getBytes().length);

        // 登录设备
        NetSDKLib.LLong m_hLoginHandle = baseModule.getNetsdkApi().CLIENT_LoginWithHighLevelSecurity(loginInput, loginOutput);

        if (m_hLoginHandle.longValue() == 0) {
            log.error("登录设备失败，错误码：" + ENUMERROR.getFullError());
            return false;
        } else {
            m_hLoginHandleMap.put(ip, m_hLoginHandle);
            log.error("登录成功");
            return true;
        }
    }

    private static int getDaHuaCommand(String command) {
        return DaHuaCommand.getDaHuaCommand(command);
    }

    private static void logoutAndCleanUp(boolean bInit, boolean bLogin) {
        if (bLogin) {
            if (!m_hLoginHandleMap.isEmpty()) {
                Iterator<String> iterator = m_hLoginHandleMap.keySet().iterator();
                while (iterator.hasNext()) {
                    String ip = iterator.next();
                    if (baseModule.getNetsdkApi().CLIENT_Logout(m_hLoginHandleMap.get(ip))) {
                        log.info("logout success");
                        iterator.remove();
                    } else {
                        log.error("logout failed");
                    }
                }
            }
        }
        if (bInit) {
            baseModule.clean();
            log.info("cleanup success");
        }
    }
}
